use std::fmt;

use rand::RngCore;

use aspect_codec::prelude::*;

use super::errors::Error;

pub const ID_LEN: usize = 16;

#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct QueueId([u8; ID_LEN]);

impl QueueId {
    pub fn to_vec(&self) -> Vec<u8> {
        self.0.to_vec()
    }
}

impl From<[u8; ID_LEN]> for QueueId {
    fn from(value: [u8; ID_LEN]) -> Self {
        Self(value)
    }
}

impl AsRef<[u8]> for QueueId {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl fmt::Debug for QueueId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let h = hex::encode(&self.0);
        f.write_str(&h)
    }
}

impl fmt::Display for QueueId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        <Self as fmt::Debug>::fmt(self, f)
    }
}

impl TryFrom<&[u8]> for QueueId {
    type Error = Error;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        let buf = TryFrom::try_from(value).map_err(|_| Error::QueueIdMalformed)?;
        Ok(Self(buf))
    }
}

aspect_codec::inst_wrapper_codec!(QueueId => [u8; ID_LEN]);

#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct ReaderId([u8; ID_LEN]);

impl ReaderId {
    pub fn new_rand() -> Self {
        let mut buf = [0u8; ID_LEN];
        let mut rng = rand::rngs::OsRng;
        rng.fill_bytes(&mut buf);
        ReaderId::from(buf)
    }

    pub fn to_vec(&self) -> Vec<u8> {
        self.0.to_vec()
    }
}

impl From<[u8; ID_LEN]> for ReaderId {
    fn from(value: [u8; ID_LEN]) -> Self {
        Self(value)
    }
}

impl fmt::Debug for ReaderId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let h = hex::encode(&self.0);
        f.write_str(&h)
    }
}

impl fmt::Display for ReaderId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        <Self as fmt::Debug>::fmt(self, f)
    }
}

impl TryFrom<&[u8]> for ReaderId {
    type Error = Error;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        let buf = TryFrom::try_from(value).map_err(|_| Error::ReaderIdMalformed)?;
        Ok(ReaderId(buf))
    }
}

aspect_codec::inst_wrapper_codec!(ReaderId => [u8; ID_LEN]);
